/*
 * Copyright 2024 The CHANGLEI Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

part of '../px.dart';

/// 像素大小
extension NumToPx on num {
  /// 像素大小
  Px get px {
    if (this is double) {
      return (this as double).px;
    } else {
      return (this as int).px;
    }
  }

  /// 像素大小
  PxDouble toPx(BuildContext context) {
    return (this * context.density).px;
  }
}

/// 像素大小
extension IntToPx on int {
  /// 像素大小
  PxInt get px => PxInt(this);
}

/// 像素大小
extension DoubleToPx on double {
  /// 像素大小
  PxDouble get px => PxDouble(this);
}

/// Created by changlei on 2024/4/23.
///
/// See [num]
abstract class Px implements _Px<num>, Comparable<Px> {
  /// See [num.new]
  const factory Px(double _) = PxDouble;

  /// See [num.new]
  const Px._(this._);

  /// 原始值
  final num _;

  /// zero
  static const zero = Px(0);

  @override
  int compareTo(Px other) => _.compareTo(other._);

  /// See [num.+]
  Px operator +(Px other) => (_ + other._).px;

  /// See [num.-]
  Px operator -(Px other) => (_ - other._).px;

  /// See [num.*]
  Px operator *(Px other) => (_ * other._).px;

  /// See [num.%]
  Px operator %(Px other) => (_ % other._).px;

  /// See [num./]
  PxDouble operator /(Px other) => (_ / other._).px;

  /// See [num.~/]
  PxInt operator ~/(Px other) => (_ ~/ other._).px;

  /// See [num.-]
  Px operator -();

  /// See [num.remainder]
  Px remainder(Px other) => _.remainder(other._).px;

  /// See [num.<]
  bool operator <(Px other) => _ < other._;

  /// See [num.<=]
  bool operator <=(Px other) => _ <= other._;

  /// See [num.>]
  bool operator >(Px other) => _ > other._;

  /// See [num.>=]
  bool operator >=(Px other) => _ >= other._;

  /// See [num.isNaN]
  bool get isNaN => _.isNaN;

  /// See [num.isNegative]
  bool get isNegative => _.isNegative;

  /// See [num.isInfinite]
  bool get isInfinite => _.isInfinite;

  /// See [num.isFinite]
  bool get isFinite => _.isFinite;

  /// See [num.abs]
  Px abs();

  /// See [num.sign]
  num get sign;

  /// See [num.round]
  PxInt round() => _.round().px;

  /// See [num.ceil]
  PxInt floor() => _.floor().px;

  /// See [num.ceil]
  PxInt ceil() => _.ceil().px;

  /// See [num.truncate]
  PxInt truncate() => _.truncate().px;

  /// See [num.roundToDouble]
  PxDouble roundToDouble() => _.roundToDouble().px;

  /// See [num.floorToDouble]
  PxDouble floorToDouble() => _.floorToDouble().px;

  /// See [num.ceilToDouble]
  PxDouble ceilToDouble() => _.ceilToDouble().px;

  /// See [num.truncateToDouble]
  PxDouble truncateToDouble() => _.truncateToDouble().px;

  /// See [num.clamp]
  Px clamp(Px lowerLimit, Px upperLimit) => _.clamp(lowerLimit._, upperLimit._).px;

  /// See [num.toInt]
  PxInt toInt() => _.toInt().px;

  /// See [num.toDouble]
  PxDouble toDouble() => _.toDouble().px;

  /// See [num.toStringAsFixed]
  String toStringAsFixed(int fractionDigits) => _.toStringAsFixed(fractionDigits);

  /// See [num.toStringAsExponential]
  String toStringAsExponential([int? fractionDigits]) => _.toStringAsExponential(fractionDigits);

  /// See [num.toStringAsPrecision]
  String toStringAsPrecision(int precision) => _.toStringAsPrecision(precision);

  /// See [num.toString]
  static Px parse(String input, [Px Function(String input)? onError]) {
    final Px? result = tryParse(input);
    if (result != null) {
      return result;
    }
    throw FormatException(input);
  }

  /// See [num.tryParse]
  static Px? tryParse(String input) {
    final String source = input.trim();
    return (int.tryParse(source) ?? double.tryParse(source))?.px;
  }

  /// See [lerpDouble]
  static PxDouble? lerp(PxDouble? a, PxDouble? b, double t) {
    if (b == null) {
      if (a == null) {
        return null;
      } else {
        return a * (1.0 - t).px;
      }
    } else {
      if (a == null) {
        return b * t.px;
      } else {
        return _lerpPxDouble(a, b, t);
      }
    }
  }

  @override
  double toDp(BuildContext context) => _ / context.density;

  @override
  bool operator ==(Object other) =>
      identical(this, other) || other is Px && runtimeType == other.runtimeType && _ == other._;

  @override
  int get hashCode => _.hashCode;

  @override
  String toString() => _.toString();

  /// call
  Result to<Result extends num>() {
    if (Result == int) {
      return _.toInt() as Result;
    } else {
      return _.toDouble() as Result;
    }
  }
}

/// See [int]
class PxInt extends Px {
  /// See [int.new]
  // ignore: no_wildcard_variable_uses
  const PxInt(int _) : super._(_);

  @override
  int get _ => super._ as int;

  /// zero
  static const zero = PxInt(0);

  /// See [int.&]
  PxInt operator &(PxInt other) => (_ & (other._)).px;

  /// See [int.|]
  PxInt operator |(PxInt other) => (_ | (other._)).px;

  /// See [int.~]
  PxInt operator ^(PxInt other) => (_ ^ (other._)).px;

  /// See [int.~]
  PxInt operator ~() => (~_).px;

  /// See [int.<<]
  PxInt operator <<(int shiftAmount) => (_ << shiftAmount).px;

  /// See [int.>>]
  PxInt operator >>(int shiftAmount) => (_ >> shiftAmount).px;

  /// See [int.>>>]
  PxInt operator >>>(int shiftAmount) => (_ >>> shiftAmount).px;

  /// See [int.modPow]
  PxInt modPow(int exponent, PxInt modulus) => _.modPow(exponent, modulus._).px;

  /// See [int.modInverse]
  PxInt modInverse(PxInt modulus) => _.modInverse(modulus._).px;

  /// See [int.gcd]
  PxInt gcd(PxInt other) => _.gcd(other._).px;

  /// See [int.isEven]
  bool get isEven => _.isEven;

  /// See [int.isOdd]
  bool get isOdd => _.isOdd;

  /// See [int.bitLength]
  int get bitLength => _.bitLength;

  /// See [int.toUnsigned]
  PxInt toUnsigned(int width) => _.toUnsigned(width).px;

  /// See [int.toSigned]
  PxInt toSigned(int width) => _.toSigned(width).px;

  @override
  PxInt operator -() => (-_).px;

  @override
  PxInt abs() => _.abs().px;

  @override
  int get sign => _.sign;

  @override
  PxInt round() => _.px;

  @override
  PxInt floor() => _.px;

  @override
  PxInt ceil() => _.px;

  @override
  PxInt truncate() => _.px;

  @override
  PxDouble roundToDouble() => _.roundToDouble().px;

  @override
  PxDouble floorToDouble() => _.floorToDouble().px;

  @override
  PxDouble ceilToDouble() => _.ceilToDouble().px;

  @override
  PxDouble truncateToDouble() => _.truncateToDouble().px;

  @override
  String toString() => _.toString();

  /// See [int.toRadixString]
  String toRadixString(int radix) => _.toRadixString(radix);

  /// See [int.parse]
  static PxInt parse(String source, {int? radix}) => int.parse(source, radix: radix).px;

  /// See [int.tryParse]
  external static PxInt? tryParse(String source, {int? radix});
}

/// See [double]
class PxDouble extends Px {
  /// See [double.new]
  // ignore: no_wildcard_variable_uses
  const PxDouble(double _) : super._(_);

  @override
  double get _ => super._ as double;

  /// zero
  static const zero = PxDouble(0);

  /// See [double.nan]
  static const nan = PxDouble(double.nan);

  /// See [double.infinity]
  static const infinity = PxDouble(double.infinity);

  /// See [double.negativeInfinity]
  static const negativeInfinity = PxDouble(double.negativeInfinity);

  /// See [double.minPositive]
  static const minPositive = PxDouble(double.minPositive);

  /// See [double.maxFinite]
  static const maxFinite = PxDouble(double.maxFinite);

  @override
  PxDouble remainder(Px other) => _.remainder(other._).px;

  @override
  PxDouble operator +(Px other) => (_ + other._).px;

  @override
  PxDouble operator -(Px other) => (_ - other._).px;

  @override
  PxDouble operator *(Px other) => (_ * other._).px;

  @override
  PxDouble operator %(Px other) => (_ % other._).px;

  @override
  PxDouble operator /(Px other) => (_ / other._).px;

  @override
  PxInt operator ~/(Px other) => (_ ~/ other._).px;

  @override
  PxDouble operator -() => (-_).px;

  @override
  PxDouble abs() => _.abs().px;

  @override
  double get sign => _.sign;

  @override
  PxInt round() => _.round().px;

  @override
  PxInt floor() => _.floor().px;

  @override
  PxInt ceil() => _.ceil().px;

  @override
  PxInt truncate() => _.truncate().px;

  @override
  PxDouble roundToDouble() => _.roundToDouble().px;

  @override
  PxDouble floorToDouble() => _.floorToDouble().px;

  @override
  PxDouble ceilToDouble() => _.ceilToDouble().px;

  @override
  PxDouble truncateToDouble() => _.truncateToDouble().px;

  @override
  String toString() => _.toString();

  /// See [double.parse]
  static PxDouble parse(String source) => double.parse(source).px;

  /// See [double.tryParse]
  static PxDouble? tryParse(String source) => double.tryParse(source)?.px;
}
